package com.li.away3dlite.camera
{
import away3dlite.cameras.Camera3D;

import com.li.away3dlite.events.OldCameraControllerEvent;
import com.li.general.events.MouseCaptureEvent;
import com.li.general.keyboard.KeyManager;
import com.li.general.mouse.MouseCapture;

import flash.events.EventDispatcher;
import flash.geom.Vector3D;

/**
 * @author Li
 */
public class OldPanZoomOrbitCameraController extends EventDispatcher
{
    // ------------------------------------------------------------------------------------
    // Public fields.
    // ------------------------------------------------------------------------------------

    public static const MODE_PAN:uint = 0;
    public static const MODE_ZOOM:uint = 1;
    public static const MODE_ORBIT:uint = 2;

    // ------------------------------------------------------------------------------------
    // Private fields.
    // ------------------------------------------------------------------------------------

    private var _camera:Camera3D;
    private var _mouseCapture:MouseCapture;
    private var _orbitController:OldOrbitCameraController;
    private var _mode:int = -1;
    private var _right:Vector3D;
    private var _up:Vector3D;

    private var _mouseDeltaToAzimuthFactor:Number = 0.0025;
    private var _mouseDeltaToElevationFactor:Number = 0.0025;
    private var _mouseDeltaToRadiusFactor:Number = 5;

    private var _mouseDeltaToCenterXFactor:Number = 1;
    private var _mouseDeltaToCenterYFactor:Number = 1;

    private var _keyZoomSpeed:Number = 50;

    private var _keyElevationSpeed:Number = 0.05;
    private var _keyAzimuthSpeed:Number = 0.05;

    private var _keyPanXSpeed:Number = 10;
    private var _keyPanYSpeed:Number = 10;

    // ------------------------------------------------------------------------------------
    // Public methods.
    // ------------------------------------------------------------------------------------

    public function set maxRadius(value:Number):void
    {
        _orbitController.maxRadius = value;
    }

    public function OldPanZoomOrbitCameraController(camera:Camera3D, mouseCapture:MouseCapture)
    {
        super();

        _camera = camera;
        _mouseCapture = mouseCapture;

        // Initialize the orbit controller.
        _orbitController = new OldOrbitCameraController(camera);
        _orbitController.addEventListener(OldCameraControllerEvent.MOTION_STARTED, controllerMotionStartedHandler);
        _orbitController.addEventListener(OldCameraControllerEvent.MOTION_FINISHED, controllerMotionFinishedHandler);

        // Attach listeners to mouse capture.
        _mouseCapture.addEventListener(MouseCaptureEvent.MOUSE_DELTA, captureMouseDeltaHandler);

        mode = MODE_ORBIT;
    }

    public function set center(value:Vector3D):void
    {
        _orbitController.center = value;
    }

    public function get center():Vector3D
    {
        return _orbitController.center;
    }

    public function set maxElevationFromPosition(value:Vector3D):void
    {
        _orbitController.maxElevationFromPosition = value;
    }

    public function set mode(value:uint):void
    {
        _mode = value;
    }

    public function get moving():Boolean
    {
        return _orbitController.moving;
    }

    public function update():void
    {
        updateKeyboardInput();
        _orbitController.update();
    }

    // ------------------------------------------------------------------------------------
    // Private methods.
    // ------------------------------------------------------------------------------------

    private function updateKeyboardInput():void
    {
        // PAN.
        var dx:Number = 0;
        var dy:Number = 0;
        if(KeyManager.instance.getKeyIsDown(KeyManager.KEY_UP))
            dy += -_keyPanYSpeed;
        else if(KeyManager.instance.getKeyIsDown(KeyManager.KEY_DOWN))
            dy += _keyPanYSpeed;
        if(KeyManager.instance.getKeyIsDown(KeyManager.KEY_LEFT))
            dx += -_keyPanXSpeed;
        else if(KeyManager.instance.getKeyIsDown(KeyManager.KEY_RIGHT))
            dx += _keyPanXSpeed;
        moveOrbitCenterOnCameraPlane(dx, dy);

        // ZOOM.
        if(KeyManager.instance.getKeyIsDown(KeyManager.KEY_Z))
            _orbitController.moveRadius(-_keyZoomSpeed);
        else if(KeyManager.instance.getKeyIsDown(KeyManager.KEY_X))
            _orbitController.moveRadius(_keyZoomSpeed);

        // ORBIT.
        if(KeyManager.instance.getKeyIsDown(KeyManager.KEY_W))
            _orbitController.moveElevation(-_keyElevationSpeed);
        else if(KeyManager.instance.getKeyIsDown(KeyManager.KEY_S))
            _orbitController.moveElevation(_keyElevationSpeed);
        if(KeyManager.instance.getKeyIsDown(KeyManager.KEY_A))
            _orbitController.moveAzimuth(_keyAzimuthSpeed);
        else if(KeyManager.instance.getKeyIsDown(KeyManager.KEY_D))
            _orbitController.moveAzimuth(-_keyAzimuthSpeed);
    }

    private function evaluateMouseInputAsPanMode(deltaX:Number, deltaY:Number):void
    {
        moveOrbitCenterOnCameraPlane(deltaX*_mouseDeltaToCenterXFactor, deltaY*_mouseDeltaToCenterYFactor);
    }

    private function evaluateMouseInputAsZoomMode(deltaX:Number, deltaY:Number):void
    {
        deltaX;
        _orbitController.moveRadius(deltaY*_mouseDeltaToRadiusFactor);
    }

    private function evaluateMouseInputAsOrbitMode(deltaX:Number, deltaY:Number):void
    {
        _orbitController.moveAzimuth(-deltaX*_mouseDeltaToAzimuthFactor);
        _orbitController.moveElevation(-deltaY*_mouseDeltaToElevationFactor);
    }

    private function moveOrbitCenterOnCameraPlane(deltaX:Number, deltaY:Number):void
    {
        if(deltaX == 0 && deltaY == 0)
            return;

        _right = new Vector3D(deltaX, 0, 0);
        _right = _camera.screenMatrix3D.transformVector(_right);
        _right.x -= _camera.x;
        _right.y -= _camera.y;
        _right.z -= _camera.z;

        _up = new Vector3D(0, deltaY, 0);
        _up = _camera.screenMatrix3D.transformVector(_up);
        _up.x -= _camera.x;
        _up.y -= _camera.y;
        _up.z -= _camera.z;

        var dx:Number = _right.x + _up.x;
        var dy:Number = _right.y + _up.y;
        var dz:Number = _right.z + _up.z;

        _orbitController.moveCenterX(dx);
        _orbitController.moveCenterY(dy);
        _orbitController.moveCenterZ(dz);
    }

    // ------------------------------------------------------------------------------------
    // Event handlers.
    // ------------------------------------------------------------------------------------

    private function captureMouseDeltaHandler(event:MouseCaptureEvent):void
    {
        //            trace("PanZoomOrbitCameraController.as - receiving mouse delta.");

        // Use mouse input to react to the appropriate mode.
        if(_mode == MODE_PAN)
            evaluateMouseInputAsPanMode(event.deltaX, event.deltaY);
        else if(_mode == MODE_ZOOM)
            evaluateMouseInputAsZoomMode(event.deltaX, event.deltaY);
        else if(_mode == MODE_ORBIT)
            evaluateMouseInputAsOrbitMode(event.deltaX, -event.deltaY);
    }

    private function controllerMotionStartedHandler(event:OldCameraControllerEvent):void
    {
        dispatchEvent(event.clone());
    }

    private function controllerMotionFinishedHandler(event:OldCameraControllerEvent):void
    {
        dispatchEvent(event.clone());
    }
}
}
